package luminus.origin.commands.teleport

import luminus.origin.utils.Configurations.teleport
import luminus.origin.utils.isSafeLocation
import org.bukkit.Bukkit
import org.bukkit.entity.Player
import taboolib.common.platform.command.CommandBody
import taboolib.common.platform.command.CommandHeader
import taboolib.common.platform.command.PermissionDefault
import taboolib.common.platform.command.mainCommand
import taboolib.common.platform.function.submit
import taboolib.platform.util.sendLang
import java.util.*
import java.util.concurrent.ConcurrentHashMap

object Ask {

    private val requests = ConcurrentHashMap<UUID, Pair<UUID, RequestType>>()
    private val REQUEST_TIMEOUT by lazy { teleport.getLong("requestTimeout") * 20L }

    @CommandHeader(
        "teleportask", ["tpask", "tpa"],
        permission = "luminus.tpa",
        permissionDefault = PermissionDefault.TRUE
    )
    object TeleportAsk {
        @CommandBody
        val main = mainCommand {
            execute<Player> { sender, _, _ ->
                sender.sendLang("teleport-tpa-ask-usage")
            }
            dynamic("target") {
                suggestion<Player>(uncheck = true) { _, _ ->
                    Bukkit.getOnlinePlayers().map { it.name }
                }
                execute<Player> { sender, _, argument ->
                    val target = Bukkit.getPlayer(argument) ?: run {
                        sender.sendLang("teleport-tpa-player-not-found", argument)
                        return@execute
                    }
                    var exist = false
                    requests.forEach { (uuid, _) -> if (uuid == sender.uniqueId) exist=true }

                    if (exist) {
                        sender.sendLang("teleport-tpa-exist-request")
                    }

                    if (sender == target) {
                        sender.sendLang("teleport-tpa-ask-self")
                        return@execute
                    }

                    sendRequest(sender, target, RequestType.NORMAL)
                }
            }
            literal("here") {
                dynamic("target") {
                    suggestion<Player>(uncheck = true) { _, _ ->
                        Bukkit.getOnlinePlayers().map { it.name }
                    }
                    execute<Player> { sender, _, argument ->
                        val target = Bukkit.getPlayer(argument) ?: run {
                            sender.sendLang("teleport-tpa-player-not-found", argument)
                            return@execute
                        }
                        var exist = false
                        requests.forEach { (uuid, _) -> if (uuid == sender.uniqueId) exist=true }

                        if (exist) {
                            sender.sendLang("teleport-tpa-exist-request")
                        }

                        if (sender == target) {
                            sender.sendLang("teleport-tpa-ask-self")
                            return@execute
                        }

                        sendRequest(sender, target, RequestType.HERE)
                    }
                }
            }
        }
    }

    @CommandHeader(
        "teleportaccept", ["tpaccept", "tpyes"],
        permission = "luminus.tpa",
        permissionDefault = PermissionDefault.TRUE
    )
    object TeleportAccept {
        @CommandBody
        val main = mainCommand {
            execute<Player> { sender, _, _ ->
                handleRequest(sender, ReactionType.ACCEPT)
            }
            dynamic("target") {
                suggestion<Player>(uncheck = true) { _, _ ->
                    requests.entries
                        .filter { it.value.first == it.key }.mapNotNull { Bukkit.getPlayer(it.key)?.name }
                }
                execute<Player> { sender, _, argument ->
                    val target = Bukkit.getPlayer(argument) ?: run {
                        sender.sendLang("teleport-tpa-player-not-found", argument)
                        return@execute
                    }

                    if (requests[target.uniqueId]?.first != sender.uniqueId) {
                        sender.sendLang("teleport-tpa-no-pending", target.name)
                        return@execute
                    }

                    handleRequest(sender, ReactionType.ACCEPT, target)
                }
            }
        }
    }

    @CommandHeader(
        "teleportdeny", ["tpdeny", "tpno"],
        permission = "luminus.tpa",
        permissionDefault = PermissionDefault.TRUE
    )
    object TeleportDeny {
        @CommandBody
        val main = mainCommand {
            execute<Player> { sender, _, _ ->
                handleRequest(sender, ReactionType.DENY)
            }
            dynamic("target") {
                suggestion<Player>(uncheck = true) { _, _ ->
                    requests.entries
                        .filter { it.value.first == it.key }.mapNotNull { Bukkit.getPlayer(it.key)?.name }
                }
                execute<Player> { sender, _, argument ->
                    val target = Bukkit.getPlayer(argument) ?: run {
                        sender.sendLang("teleport-tpa-player-not-found", argument)
                        return@execute
                    }

                    if (requests[target.uniqueId]?.first != sender.uniqueId) {
                        sender.sendLang("teleport-tpa-no-pending", target.name)
                        return@execute
                    }

                    handleRequest(sender, ReactionType.DENY, target)
                }
            }
        }
    }

    private fun sendRequest(sender: Player, target: Player, type: RequestType) {
        requests.entries.removeAll { it.value.first == sender.uniqueId }

        requests[target.uniqueId] = sender.uniqueId to type

        when (type) {
            RequestType.NORMAL -> {
                sender.sendLang("teleport-tpa-request-sent", target.name)
                target.sendLang("teleport-tpa-request-received", sender.name)
            }
            RequestType.HERE -> {
                sender.sendLang("teleport-tpa-request-here-sent", target.name)
                target.sendLang("teleport-tpa-request-here-received", sender.name)
            }
        }

        submit(delay = REQUEST_TIMEOUT) {
            if (requests.remove(target.uniqueId)?.first == sender.uniqueId) {
                sender.sendLang("teleport-tpa-request-timeout-sender", target.name)
                target.sendLang("teleport-tpa-request-timeout-receiver", sender.name)
            }
        }
    }

    private fun handleRequest(receiver: Player, reaction: ReactionType, specificSender: Player? = null) {
        val pendingRequests = requests.entries.filter {
            it.value.first == receiver.uniqueId &&
                    (specificSender == null || it.key == specificSender.uniqueId)
        }

        if (pendingRequests.isEmpty()) {
            receiver.sendLang("teleport-tpa-no-pending-requests")
            return
        }

        // 避免bug(生草
        pendingRequests.lastOrNull()?.let { (targetId, requestInfo) ->
            val (senderId, type) = requestInfo
            val sender = Bukkit.getPlayer(senderId) ?: return@let
            val target = Bukkit.getPlayer(targetId) ?: return@let

            requests.remove(targetId)


            when (reaction) {
                ReactionType.ACCEPT -> {
                    when (type) {
                        RequestType.NORMAL -> {
                            if (!target.location.isSafeLocation()){
                                sender.sendLang("teleport-tpa-unsafe-location")
                                return@let
                            }
                            sender.teleport(target.location)
                            sender.sendLang("teleport-tpa-request-accepted-sender", target.name)
                            target.sendLang("teleport-tpa-request-accepted-receiver", sender.name)
                            return@let
                        }
                        RequestType.HERE -> {
                            if (!target.location.isSafeLocation()){
                                sender.sendLang("teleport-tpa-unsafe-location")
                                return@let
                            }
                            target.teleport(sender.location)
                            sender.sendLang("teleport-tpa-request-here-accepted-sender", target.name)
                            target.sendLang("teleport-tpa-request-here-accepted-receiver", sender.name)
                            return@let
                        }
                    }
                }
                ReactionType.DENY -> {
                    sender.sendLang("teleport-tpa-request-denied-sender", target.name)
                    target.sendLang("teleport-tpa-request-denied-receiver", sender.name)
                    return@let
                }
                ReactionType.TIMEOUT -> {
                    // already handled so jump
                }
            }
        }
    }

    enum class RequestType {
        NORMAL,
        HERE
    }

    enum class ReactionType {
        ACCEPT,
        DENY,
        TIMEOUT
    }
}